home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / gr564s.zip / SRC / RCSLEX.C < prev    next >
C/C++ Source or Header  |  1992-09-05  |  28KB  |  1,288 lines

  1. /*
  2.  *                     RCS file input
  3.  */
  4. /*********************************************************************************
  5.  *                     Lexical Analysis.
  6.  *                     hashtable, Lexinit, nextlex, getlex, getkey,
  7.  *                     getid, getnum, readstring, printstring, savestring,
  8.  *                     checkid, fatserror, error, faterror, warn, diagnose
  9.  *                     Testprogram: define LEXDB
  10.  *********************************************************************************
  11.  */
  12.  
  13. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  14.    Copyright 1990, 1991, 1992 by Paul Eggert
  15.    Distributed under license by the Free Software Foundation, Inc.
  16.  
  17. This file is part of RCS.
  18.  
  19. RCS is free software; you can redistribute it and/or modify
  20. it under the terms of the GNU General Public License as published by
  21. the Free Software Foundation; either version 2, or (at your option)
  22. any later version.
  23.  
  24. RCS is distributed in the hope that it will be useful,
  25. but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27. GNU General Public License for more details.
  28.  
  29. You should have received a copy of the GNU General Public License
  30. along with RCS; see the file COPYING.  If not, write to
  31. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  32.  
  33. Report problems and direct all questions to:
  34.  
  35.     rcs-bugs@cs.purdue.edu
  36.  
  37. */
  38.  
  39.  
  40.  
  41. /* $Log: rcslex.c,v $
  42.  * Revision 5.14  1992/07/28  16:12:44  eggert
  43.  * Identifiers may now start with a digit and (unless they are symbolic names)
  44.  * may contain `.'.  Avoid `unsigned'.  Statement macro names now end in _.
  45.  *
  46.  * Revision 5.13  1992/02/17  23:02:27  eggert
  47.  * Work around NFS mmap SIGBUS problem.
  48.  *
  49.  * Revision 5.12  1992/01/06  02:42:34  eggert
  50.  * Use OPEN_O_BINARY if mode contains 'b'.
  51.  *
  52.  * Revision 5.11  1991/11/03  03:30:44  eggert
  53.  * Fix porting bug to ancient hosts lacking vfprintf.
  54.  *
  55.  * Revision 5.10  1991/10/07  17:32:46  eggert
  56.  * Support piece tables even if !has_mmap.
  57.  *
  58.  * Revision 5.9  1991/09/24  00:28:42  eggert
  59.  * Don't export errsay().
  60.  *
  61.  * Revision 5.8  1991/08/19  03:13:55  eggert
  62.  * Add eoflex(), mmap support.  Tune.
  63.  *
  64.  * Revision 5.7  1991/04/21  11:58:26  eggert
  65.  * Add MS-DOS support.
  66.  *
  67.  * Revision 5.6  1991/02/25  07:12:42  eggert
  68.  * Work around fputs bug.  strsave -> str_save (DG/UX name clash)
  69.  *
  70.  * Revision 5.5  1990/12/04  05:18:47  eggert
  71.  * Use -I for prompts and -q for diagnostics.
  72.  *
  73.  * Revision 5.4  1990/11/19  20:05:28  hammer
  74.  * no longer gives warning about unknown keywords if -q is specified
  75.  *
  76.  * Revision 5.3  1990/11/01  05:03:48  eggert
  77.  * When ignoring unknown phrases, copy them to the output RCS file.
  78.  *
  79.  * Revision 5.2  1990/09/04  08:02:27  eggert
  80.  * Count RCS lines better.
  81.  *
  82.  * Revision 5.1  1990/08/29  07:14:03  eggert
  83.  * Work around buggy compilers with defective argument promotion.
  84.  *
  85.  * Revision 5.0  1990/08/22  08:12:55  eggert
  86.  * Remove compile-time limits; use malloc instead.
  87.  * Report errno-related errors with perror().
  88.  * Ansify and Posixate.  Add support for ISO 8859.
  89.  * Use better hash function.
  90.  *
  91.  * Revision 4.6  89/05/01  15:13:07  narten
  92.  * changed copyright header to reflect current distribution rules
  93.  * 
  94.  * Revision 4.5  88/08/28  15:01:12  eggert
  95.  * Don't loop when writing error messages to a full filesystem.
  96.  * Flush stderr/stdout when mixing output.
  97.  * Yield exit status compatible with diff(1).
  98.  * Shrink stdio code size; allow cc -R; remove lint.
  99.  * 
  100.  * Revision 4.4  87/12/18  11:44:47  narten
  101.  * fixed to use "varargs" in "fprintf"; this is required if it is to
  102.  * work on a SPARC machine such as a Sun-4
  103.  * 
  104.  * Revision 4.3  87/10/18  10:37:18  narten
  105.  * Updating version numbers. Changes relative to 1.1 actually relative
  106.  * to version 4.1
  107.  * 
  108.  * Revision 1.3  87/09/24  14:00:17  narten
  109.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  110.  * warnings)
  111.  * 
  112.  * Revision 1.2  87/03/27  14:22:33  jenkins
  113.  * Port to suns
  114.  * 
  115.  * Revision 4.1  83/03/25  18:12:51  wft
  116.  * Only changed $Header to $Id.
  117.  * 
  118.  * Revision 3.3  82/12/10  16:22:37  wft
  119.  * Improved error messages, changed exit status on error to 1.
  120.  *
  121.  * Revision 3.2  82/11/28  21:27:10  wft
  122.  * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
  123.  * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
  124.  * properly in case there is an IO-error (e.g., file system full).
  125.  *
  126.  * Revision 3.1  82/10/11  19:43:56  wft
  127.  * removed unused label out:;
  128.  * made sure all calls to getc() return into an integer, not a char.
  129.  */
  130.  
  131.  
  132. /*
  133. #define LEXDB
  134. */
  135. /* version LEXDB is for testing the lexical analyzer. The testprogram
  136.  * reads a stream of lexemes, enters the revision numbers into the
  137.  * hashtable, and prints the recognized tokens. Keywords are recognized
  138.  * as identifiers.
  139.  */
  140.  
  141.  
  142.  
  143. #include "rcsbase.h"
  144.  
  145. libId(lexId, "$Id: rcslex.c,v 5.14 1992/07/28 16:12:44 eggert Exp $")
  146.  
  147. static struct hshentry *nexthsh;  /*pointer to next hash entry, set by lookup*/
  148.  
  149. enum tokens     nexttok;    /*next token, set by nextlex                    */
  150.  
  151. int             hshenter;   /*if true, next suitable lexeme will be entered */
  152.                             /*into the symbol table. Handle with care.      */
  153. int             nextc;      /*next input character, initialized by Lexinit  */
  154.  
  155. long        rcsline;    /*current line-number of input            */
  156. int             nerror;     /*counter for errors                            */
  157. int             quietflag;  /*indicates quiet mode                          */
  158. RILE *        finptr;        /*input file descriptor                */
  159.  
  160. FILE *          frewrite;   /*file descriptor for echoing input             */
  161.  
  162. FILE *        foutptr;    /* copy of frewrite, but 0 to suppress echo  */
  163.  
  164. static struct buf tokbuf;   /* token buffer                    */
  165.  
  166. char const *    NextString; /* next token                    */
  167.  
  168. /*
  169.  * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
  170.  * so hshsize should be odd.
  171.  * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
  172.  * Software--practice & experience 20, 2 (Feb 1990), 209-224.
  173.  */
  174. #ifndef hshsize
  175. #    define hshsize 511
  176. #endif
  177.  
  178. static struct hshentry *hshtab[hshsize]; /*hashtable                */
  179.  
  180. static int ignored_phrases; /* have we ignored phrases in this RCS file? */
  181.  
  182.     void
  183. warnignore()
  184. {
  185.     if (! (ignored_phrases|quietflag)) {
  186.     ignored_phrases = true;
  187.     warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString);
  188.     }
  189. }
  190.  
  191.  
  192.  
  193.     static void
  194. lookup(str)
  195.     char const *str;
  196. /* Function: Looks up the character string pointed to by str in the
  197.  * hashtable. If the string is not present, a new entry for it is created.
  198.  * In any case, the address of the corresponding hashtable entry is placed
  199.  * into nexthsh.
  200.  */
  201. {
  202.     register unsigned ihash;  /* index into hashtable */
  203.     register char const *sp;
  204.     register struct hshentry *n, **p;
  205.  
  206.         /* calculate hash code */
  207.     sp = str;
  208.         ihash = 0;
  209.     while (*sp)
  210.         ihash  =  (ihash<<2) + *sp++;
  211.     ihash %= hshsize;
  212.  
  213.     for (p = &hshtab[ihash];  ;  p = &n->nexthsh)
  214.         if (!(n = *p)) {
  215.             /* empty slot found */
  216.             *p = n = ftalloc(struct hshentry);
  217.             n->num = fstr_save(str);
  218.             n->nexthsh = nil;
  219. #            ifdef LEXDB
  220.                 VOID printf("\nEntered: %s at %u ", str, ihash);
  221. #            endif
  222.             break;
  223.         } else if (strcmp(str, n->num) == 0)
  224.             /* match found */
  225.             break;
  226.     nexthsh = n;
  227.     NextString = n->num;
  228. }
  229.  
  230.  
  231.  
  232.  
  233.  
  234.  
  235.     void
  236. Lexinit()
  237. /* Function: Initialization of lexical analyzer:
  238.  * initializes the hashtable,
  239.  * initializes nextc, nexttok if finptr != 0
  240.  */
  241. {       register int            c;
  242.  
  243.     for (c = hshsize;  0 <= --c;  ) {
  244.         hshtab[c] = nil;
  245.         }
  246.  
  247.     nerror = 0;
  248.     if (finptr) {
  249.         foutptr = 0;
  250.         hshenter = true;
  251.         ignored_phrases = false;
  252.         rcsline = 1;
  253.         bufrealloc(&tokbuf, 2);
  254.         Iget_(finptr, nextc)
  255.                 nextlex();            /*initial token*/
  256.         }
  257. }
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.     void
  266. nextlex()
  267.  
  268. /* Function: Reads the next token and sets nexttok to the next token code.
  269.  * Only if hshenter is set, a revision number is entered into the
  270.  * hashtable and a pointer to it is placed into nexthsh.
  271.  * This is useful for avoiding that dates are placed into the hashtable.
  272.  * For ID's and NUM's, NextString is set to the character string.
  273.  * Assumption: nextc contains the next character.
  274.  */
  275. {       register c;
  276.     declarecache;
  277.     register FILE *frew;
  278.         register char * sp;
  279.     char const *limit;
  280.         register enum tokens d;
  281.     register RILE *fin;
  282.  
  283.     fin=finptr; frew=foutptr;
  284.     setupcache(fin); cache(fin);
  285.     c = nextc;
  286.  
  287.     for (;;) { switch ((d = ctab[c])) {
  288.  
  289.     default:
  290.         fatserror("unknown character `%c'", c);
  291.         /*NOTREACHED*/
  292.  
  293.         case NEWLN:
  294.         ++rcsline;
  295. #               ifdef LEXDB
  296.         afputc('\n',stdout);
  297. #               endif
  298.                 /* Note: falls into next case */
  299.  
  300.         case SPACE:
  301.         GETC_(frew, c)
  302.         continue;
  303.  
  304.     case IDCHAR:
  305.     case LETTER:
  306.     case Letter:
  307.         d = ID;
  308.         /* fall into */
  309.     case DIGIT:
  310.     case PERIOD:
  311.         sp = tokbuf.string;
  312.         limit = sp + tokbuf.size;
  313.         *sp++ = c;
  314.         for (;;) {
  315.             GETC_(frew, c)
  316.             switch (ctab[c]) {
  317.                 case IDCHAR:
  318.                 case LETTER:
  319.                 case Letter:
  320.                 d = ID;
  321.                 /* fall into */
  322.                 case DIGIT:
  323.                 case PERIOD:
  324.                 *sp++ = c;
  325.                 if (limit <= sp)
  326.                     sp = bufenlarge(&tokbuf, &limit);
  327.                 continue;
  328.             }
  329.             break;
  330.                 }
  331.         *sp = 0;
  332.         if (d == DIGIT  ||  d == PERIOD) {
  333.             d = NUM;
  334.             if (hshenter) {
  335.                 lookup(tokbuf.string);
  336.                 break;
  337.             }
  338.         }
  339.         NextString = fstr_save(tokbuf.string);
  340.         break;
  341.  
  342.         case SBEGIN: /* long string */
  343.         d = STRING;
  344.                 /* note: only the initial SBEGIN has been read*/
  345.                 /* read the string, and reset nextc afterwards*/
  346.         break;
  347.  
  348.     case COLON:
  349.     case SEMI:
  350.         GETC_(frew, c)
  351.         break;
  352.     } break; }
  353.     nextc = c;
  354.     nexttok = d;
  355.     uncache(fin);
  356. }
  357.  
  358.     int
  359. eoflex()
  360. /*
  361.  * Yield true if we look ahead to the end of the input, false otherwise.
  362.  * nextc becomes undefined at end of file.
  363.  */
  364. {
  365.     register int c;
  366.     declarecache;
  367.     register FILE *fout;
  368.     register RILE *fin;
  369.  
  370.     c = nextc;
  371.     fin = finptr;
  372.     fout = foutptr;
  373.     setupcache(fin); cache(fin);
  374.  
  375.     for (;;) {
  376.         switch (ctab[c]) {
  377.             default:
  378.                 nextc = c;
  379.                 uncache(fin);
  380.                 return false;
  381.  
  382.             case NEWLN:
  383.                 ++rcsline;
  384.                 /* fall into */
  385.             case SPACE:
  386.                 cachegeteof_(c, {uncache(fin);return true;})
  387.                 break;
  388.         }
  389.         if (fout)
  390.             aputc_(c, fout)
  391.     }
  392. }
  393.  
  394.  
  395. int getlex(token)
  396. enum tokens token;
  397. /* Function: Checks if nexttok is the same as token. If so,
  398.  * advances the input by calling nextlex and returns true.
  399.  * otherwise returns false.
  400.  * Doesn't work for strings and keywords; loses the character string for ids.
  401.  */
  402. {
  403.         if (nexttok==token) {
  404.                 nextlex();
  405.                 return(true);
  406.         } else  return(false);
  407. }
  408.  
  409.     int
  410. getkeyopt(key)
  411.     char const *key;
  412. /* Function: If the current token is a keyword identical to key,
  413.  * advances the input by calling nextlex and returns true;
  414.  * otherwise returns false.
  415.  */
  416. {
  417.     if (nexttok==ID  &&  strcmp(key,NextString) == 0) {
  418.          /* match found */
  419.          ffree1(NextString);
  420.          nextlex();
  421.          return(true);
  422.         }
  423.         return(false);
  424. }
  425.  
  426.     void
  427. getkey(key)
  428.     char const *key;
  429. /* Check that the current input token is a keyword identical to key,
  430.  * and advance the input by calling nextlex.
  431.  */
  432. {
  433.     if (!getkeyopt(key))
  434.         fatserror("missing '%s' keyword", key);
  435. }
  436.  
  437.     void
  438. getkeystring(key)
  439.     char const *key;
  440. /* Check that the current input token is a keyword identical to key,
  441.  * and advance the input by calling nextlex; then look ahead for a string.
  442.  */
  443. {
  444.     getkey(key);
  445.     if (nexttok != STRING)
  446.         fatserror("missing string after '%s' keyword", key);
  447. }
  448.  
  449.  
  450.     char const *
  451. getid()
  452. /* Function: Checks if nexttok is an identifier. If so,
  453.  * advances the input by calling nextlex and returns a pointer
  454.  * to the identifier; otherwise returns nil.
  455.  * Treats keywords as identifiers.
  456.  */
  457. {
  458.     register char const *name;
  459.         if (nexttok==ID) {
  460.                 name = NextString;
  461.                 nextlex();
  462.                 return name;
  463.         } else  return nil;
  464. }
  465.  
  466.  
  467. struct hshentry * getnum()
  468. /* Function: Checks if nexttok is a number. If so,
  469.  * advances the input by calling nextlex and returns a pointer
  470.  * to the hashtable entry. Otherwise returns nil.
  471.  * Doesn't work if hshenter is false.
  472.  */
  473. {
  474.         register struct hshentry * num;
  475.         if (nexttok==NUM) {
  476.                 num=nexthsh;
  477.                 nextlex();
  478.                 return num;
  479.         } else  return nil;
  480. }
  481.  
  482.     struct cbuf
  483. getphrases(key)
  484.     char const *key;
  485. /* Get a series of phrases that do not start with KEY, yield resulting buffer.
  486.  * Stop when the next phrase starts with a token that is not an identifier,
  487.  * or is KEY.
  488.  * Assume !foutptr.
  489.  */
  490. {
  491.     declarecache;
  492.     register int c;
  493.     register char *p;
  494.     char const *limit;
  495.     register char const *ki, *kn;
  496.     struct cbuf r;
  497.     struct buf b;
  498.     register RILE *fin;
  499.  
  500.     if (nexttok!=ID  ||  strcmp(NextString,key) == 0) {
  501.     r.string = 0;
  502.     r.size = 0;
  503.     return r;
  504.     } else {
  505.     warnignore();
  506.     fin = finptr;
  507.     setupcache(fin); cache(fin);
  508.     bufautobegin(&b);
  509.     bufscpy(&b, NextString);
  510.     ffree1(NextString);
  511.     p = b.string + strlen(b.string);
  512.     limit = b.string + b.size;
  513.     c = nextc;
  514.     for (;;) {
  515.         for (;;) {
  516.         if (limit <= p)
  517.             p = bufenlarge(&b, &limit);
  518.         *p++ = c;
  519.         switch (ctab[c]) {
  520.             default:
  521.             fatserror("unknown character `%c'", c);
  522.             /*NOTREACHED*/
  523.             case NEWLN:
  524.             ++rcsline;
  525.             /* fall into */
  526.             case COLON: case DIGIT: case LETTER: case Letter:
  527.             case PERIOD: case SPACE:
  528.             cacheget_(c)
  529.             continue;
  530.             case SBEGIN: /* long string */
  531.             for (;;) {
  532.                 for (;;) {
  533.                 if (limit <= p)
  534.                     p = bufenlarge(&b, &limit);
  535.                 cacheget_(c)
  536.                 *p++ = c;
  537.                 switch (c) {
  538.                     case '\n':
  539.                     ++rcsline;
  540.                     /* fall into */
  541.                     default:
  542.                     continue;
  543.  
  544.                     case SDELIM:
  545.                     break;
  546.                 }
  547.                 break;
  548.                 }
  549.                 cacheget_(c)
  550.                 if (c != SDELIM)
  551.                 break;
  552.                 if (limit <= p)
  553.                 p = bufenlarge(&b, &limit);
  554.                 *p++ = c;
  555.             }
  556.             continue;
  557.             case SEMI:
  558.             cacheget_(c)
  559.             if (ctab[c] == NEWLN) {
  560.                 ++rcsline;
  561.                 if (limit <= p)
  562.                 p = bufenlarge(&b, &limit);
  563.                 *p++ = c;
  564.                 cacheget_(c)
  565.             }
  566.             for (;;) {
  567.                 switch (ctab[c]) {
  568.                 case NEWLN:
  569.                     ++rcsline;
  570.                     /* fall into */
  571.                 case SPACE:
  572.                     cacheget_(c)
  573.                     continue;
  574.  
  575.                 default: break;
  576.                 }
  577.                 break;
  578.             }
  579.             break;
  580.         }
  581.         break;
  582.         }
  583.         if (ctab[c] == Letter) {
  584.             for (kn = key;  c && *kn==c;  kn++)
  585.             cacheget_(c)
  586.             if (!*kn)
  587.             switch (ctab[c]) {
  588.                 case DIGIT: case LETTER: case Letter:
  589.                 case IDCHAR: case PERIOD:
  590.                 break;
  591.                 default:
  592.                 nextc = c;
  593.                 NextString = fstr_save(key);
  594.                 nexttok = ID;
  595.                 uncache(fin);
  596.                 goto returnit;
  597.             }
  598.             for (ki=key; ki<kn; ) {
  599.             if (limit <= p)
  600.                 p = bufenlarge(&b, &limit);
  601.             *p++ = *ki++;
  602.             }
  603.         } else {
  604.             nextc = c;
  605.             uncache(fin);
  606.             nextlex();
  607.             break;
  608.         }
  609.     }
  610.     returnit:
  611.     return bufremember(&b, (size_t)(p - b.string));
  612.     }
  613. }
  614.  
  615.  
  616.     void
  617. readstring()
  618. /* skip over characters until terminating single SDELIM        */
  619. /* If foutptr is set, copy every character read to foutptr.    */
  620. /* Does not advance nextlex at the end.                        */
  621. {       register c;
  622.     declarecache;
  623.     register FILE *frew;
  624.     register RILE *fin;
  625.     fin=finptr; frew=foutptr;
  626.     setupcache(fin); cache(fin);
  627.     for (;;) {
  628.         GETC_(frew, c)
  629.         switch (c) {
  630.             case '\n':
  631.             ++rcsline;
  632.             break;
  633.  
  634.             case SDELIM:
  635.             GETC_(frew, c)
  636.             if (c != SDELIM) {
  637.                 /* end of string */
  638.                 nextc = c;
  639.                 uncache(fin);
  640.                 return;
  641.             }
  642.             break;
  643.         }
  644.     }
  645. }
  646.  
  647.  
  648.     void
  649. printstring()
  650. /* Function: copy a string to stdout, until terminated with a single SDELIM.
  651.  * Does not advance nextlex at the end.
  652.  */
  653. {
  654.         register c;
  655.     declarecache;
  656.     register FILE *fout;
  657.     register RILE *fin;
  658.     fin=finptr;
  659.     fout = stdout;
  660.     setupcache(fin); cache(fin);
  661.     for (;;) {
  662.         cacheget_(c)
  663.         switch (c) {
  664.             case '\n':
  665.             ++rcsline;
  666.             break;
  667.             case SDELIM:
  668.             cacheget_(c)
  669.             if (c != SDELIM) {
  670.                                 nextc=c;
  671.                 uncache(fin);
  672.                                 return;
  673.                         }
  674.             break;
  675.                 }
  676.         aputc_(c,fout)
  677.         }
  678. }
  679.  
  680.  
  681.  
  682.     struct cbuf
  683. savestring(target)
  684.     struct buf *target;
  685. /* Copies a string terminated with SDELIM from file finptr to buffer target.
  686.  * Double SDELIM is replaced with SDELIM.
  687.  * If foutptr is set, the string is also copied unchanged to foutptr.
  688.  * Does not advance nextlex at the end.
  689.  * Yield a copy of *TARGET, except with exact length.
  690.  */
  691. {
  692.         register c;
  693.     declarecache;
  694.     register FILE *frew;
  695.     register char *tp;
  696.     register RILE *fin;
  697.     char const *limit;
  698.     struct cbuf r;
  699.  
  700.     fin=finptr; frew=foutptr;
  701.     setupcache(fin); cache(fin);
  702.     tp = target->string;  limit = tp + target->size;
  703.     for (;;) {
  704.         GETC_(frew, c)
  705.         switch (c) {
  706.             case '\n':
  707.             ++rcsline;
  708.             break;
  709.             case SDELIM:
  710.             GETC_(frew, c)
  711.             if (c != SDELIM) {
  712.                                 /* end of string */
  713.                                 nextc=c;
  714.                 r.string = target->string;
  715.                 r.size = tp - r.string;
  716.                 uncache(fin);
  717.                 return r;
  718.                         }
  719.             break;
  720.                 }
  721.         if (tp == limit)
  722.             tp = bufenlarge(target, &limit);
  723.         *tp++ = c;
  724.         }
  725. }
  726.  
  727.  
  728.     static char *
  729. checkidentifier(id, delimiter, dotok)
  730.     register char *id;
  731.     int delimiter;
  732.     register int dotok;
  733. /*   Function:  check whether the string starting at id is an   */
  734. /*        identifier and return a pointer to the delimiter*/
  735. /*        after the identifier.  White space, delim and 0 */
  736. /*              are legal delimiters.  Aborts the program if not*/
  737. /*              a legal identifier. Useful for checking commands*/
  738. /*        If !delim, the only delimiter is 0.        */
  739. /*        Allow '.' in identifier only if DOTOK is set.   */
  740. {
  741.         register char    *temp;
  742.     register char c;
  743.     register char delim = delimiter;
  744.     int isid = false;
  745.  
  746.     temp = id;
  747.     for (;;  id++) {
  748.         switch (ctab[(unsigned char)(c = *id)]) {
  749.             case IDCHAR:
  750.             case LETTER:
  751.             case Letter:
  752.                 isid = true;
  753.                 continue;
  754.  
  755.             case DIGIT:
  756.                 continue;
  757.  
  758.             case PERIOD:
  759.                 if (dotok)
  760.                     continue;
  761.                 break;
  762.         }
  763.         break;
  764.     }
  765.     if (     ! isid
  766.         ||     c  &&  (!delim  ||  c!=delim && c!=' ' && c!='\t' && c!='\n')
  767.     ) {
  768.                 /* append \0 to end of id before error message */
  769.         while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
  770.             id++;
  771.                 *id = '\0';
  772.         faterror("invalid %s `%s'",
  773.             dotok ? "identifier" : "symbol", temp
  774.         );
  775.     }
  776.     return id;
  777. }
  778.  
  779.     char *
  780. checkid(id, delimiter)
  781.     char *id;
  782.     int delimiter;
  783. {
  784.     return checkidentifier(id, delimiter, true);
  785. }
  786.  
  787.     char *
  788. checksym(sym, delimiter)
  789.     char *sym;
  790.     int delimiter;
  791. {
  792.     return checkidentifier(sym, delimiter, false);
  793. }
  794.  
  795.     void
  796. checksid(id)
  797.     char *id;
  798. /* Check whether the string ID is an identifier.  */
  799. {
  800.     VOID checkid(id, 0);
  801. }
  802.  
  803.     void
  804. checkssym(sym)
  805.     char *sym;
  806. {
  807.     VOID checksym(sym, 0);
  808. }
  809.  
  810.  
  811.     static RILE *
  812. #if has_mmap && large_memory
  813. fd2_RILE(fd, name, status)
  814. #else
  815. fd2RILE(fd, name, mode, status)
  816.     char const *mode;
  817. #endif
  818.     int fd;
  819.     char const *name;
  820.     register struct stat *status;
  821. {
  822.     struct stat st;
  823.  
  824.     if (!status)
  825.         status = &st;
  826.     if (fstat(fd, status) != 0)
  827.         efaterror(name);
  828.     if (!S_ISREG(status->st_mode)) {
  829.         error("`%s' is not a regular file", name);
  830.         VOID close(fd);
  831.         errno = EINVAL;
  832.         return 0;
  833.     } else {
  834.  
  835. #        if ! (has_mmap && large_memory)
  836.         FILE *stream;
  837.         if (!(stream = fdopen(fd, mode)))
  838.             efaterror(name);
  839. #        endif
  840.  
  841. #        if !large_memory
  842.         return stream;
  843. #        else
  844. #        define RILES 3
  845.         {
  846.             static RILE rilebuf[RILES];
  847.  
  848.             register RILE *f;
  849.             size_t s = status->st_size;
  850.  
  851.             if (s != status->st_size)
  852.                 faterror("`%s' is enormous", name);
  853.             for (f = rilebuf;  f->base;  f++)
  854.                 if (f == rilebuf+RILES)
  855.                     faterror("too many RILEs");
  856.             if (!s) {
  857.                 static unsigned char dummy;
  858.                 f->base = &dummy;
  859.             } else {
  860. #                if has_mmap
  861.                 catchmmapints();
  862.                 if (
  863.                     (f->base = (unsigned char *)mmap(
  864.                     (caddr_t)0, s, PROT_READ, MAP_SHARED,
  865.                     fd, (off_t)0
  866.                     )) == (unsigned char *)-1
  867.                 )
  868.                     efaterror("mmap");
  869. #                else
  870.                     f->base = tnalloc(unsigned char, s);
  871. #                endif
  872.             }
  873.             f->ptr = f->base;
  874.             f->lim = f->base + s;
  875. #            if has_mmap
  876.                 f->fd = fd;
  877. #            else
  878.                 f->readlim = f->base;
  879.                 f->stream = stream;
  880. #            endif
  881.             if_advise_access(s, f, MADV_SEQUENTIAL);
  882.             return f;
  883.         }
  884. #        endif
  885.     }
  886. }
  887.  
  888. #if !has_mmap && large_memory
  889.     int
  890. Igetmore(f)
  891.     register RILE *f;
  892. {
  893.     register fread_type r;
  894.     register size_t s = f->lim - f->readlim;
  895.  
  896.     if (BUFSIZ < s)
  897.         s = BUFSIZ;
  898.     if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
  899.         testIerror(f->stream);
  900.         f->lim = f->readlim;  /* The file might have shrunk!  */
  901.         return 0;
  902.     }
  903.     f->readlim += r;
  904.     return 1;
  905. }
  906. #endif
  907.  
  908. #if has_madvise && has_mmap && large_memory
  909.     void
  910. advise_access(f, advice)
  911.     register RILE *f;
  912.     int advice;
  913. {
  914.     if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0)
  915.         efaterror("madvise");
  916. }
  917. #endif
  918.  
  919.     RILE *
  920. #if has_mmap && large_memory
  921. I_open(name, status)
  922. #else
  923. Iopen(name, mode, status)
  924.     char const *mode;
  925. #endif
  926.     char const *name;
  927.     struct stat *status;
  928. /* Open NAME for reading, yield its descriptor, and set *STATUS.  */
  929. {
  930.     int fd = open(name, O_RDONLY
  931. #        if OPEN_O_BINARY  &&  !(has_mmap && large_memory)
  932.             |  (strchr(mode,'b') ? OPEN_O_BINARY : 0)
  933. #        endif
  934.     );
  935.  
  936.     if (fd < 0)
  937.         return 0;
  938. #    if has_mmap && large_memory
  939.         return fd2_RILE(fd, name, status);
  940. #    else
  941.         return fd2RILE(fd, name, mode, status);
  942. #    endif
  943. }
  944.  
  945.  
  946. #if !large_memory
  947. #    define Iclose(f) fclose(f)
  948. #else
  949.         static int
  950.     Iclose(f)
  951.         register RILE *f;
  952.     {
  953. #        if has_mmap
  954.         size_t s = f->lim - f->base;
  955.         if (s  &&  munmap((caddr_t)f->base, s) != 0)
  956.             return -1;
  957.         f->base = 0;
  958.         return close(f->fd);
  959. #        else
  960.         tfree(f->base);
  961.         f->base = 0;
  962.         return fclose(f->stream);
  963. #        endif
  964.     }
  965. #endif
  966.  
  967.  
  968. static int Oerrloop;
  969.  
  970.     exiting void
  971. Oerror()
  972. {
  973.     if (Oerrloop)
  974.         exiterr();
  975.     Oerrloop = true;
  976.     efaterror("output error");
  977. }
  978.  
  979. exiting void Ieof() { fatserror("unexpected end of file"); }
  980. exiting void Ierror() { efaterror("input error"); }
  981. void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
  982. void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
  983.  
  984. void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
  985. void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
  986. void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
  987. void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
  988.  
  989. #if !large_memory
  990.     void
  991. testIeof(f)
  992.     FILE *f;
  993. {
  994.     testIerror(f);
  995.     if (feof(f))
  996.         Ieof();
  997. }
  998. void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
  999. #endif
  1000.  
  1001. void eflush()
  1002. {
  1003.     if (fflush(stderr) != 0  &&  !Oerrloop)
  1004.         Oerror();
  1005. }
  1006.  
  1007. void oflush()
  1008. {
  1009.     if (fflush(workstdout ? workstdout : stdout) != 0  &&  !Oerrloop)
  1010.         Oerror();
  1011. }
  1012.  
  1013.     static exiting void
  1014. fatcleanup(already_newline)
  1015.     int already_newline;
  1016. {
  1017.     VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
  1018.     exiterr();
  1019. }
  1020.  
  1021. static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; }
  1022. static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); }
  1023.  
  1024. void eerror(s) char const *s; { enerror(errno,s); }
  1025.  
  1026.     void
  1027. enerror(e,s)
  1028.     int e;
  1029.     char const *s;
  1030. {
  1031.     errsay();
  1032.     errno = e;
  1033.     perror(s);
  1034.     eflush();
  1035. }
  1036.  
  1037. exiting void efaterror(s) char const *s; { enfaterror(errno,s); }
  1038.  
  1039.     exiting void
  1040. enfaterror(e,s)
  1041.     int e;
  1042.     char const *s;
  1043. {
  1044.     fatsay();
  1045.     errno = e;
  1046.     perror(s);
  1047.     fatcleanup(true);
  1048. }
  1049.  
  1050. #if has_prototypes
  1051.     void
  1052. error(char const *format,...)
  1053. #else
  1054.     /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
  1055. #endif
  1056. /* non-fatal error */
  1057. {
  1058.     va_list args;
  1059.     errsay();
  1060.     vararg_start(args, format);
  1061.     fvfprintf(stderr, format, args);
  1062.     va_end(args);
  1063.     afputc('\n',stderr);
  1064.     eflush();
  1065. }
  1066.  
  1067. #if has_prototypes
  1068.     exiting void
  1069. fatserror(char const *format,...)
  1070. #else
  1071.     /*VARARGS1*/ exiting void
  1072.     fatserror(format, va_alist) char const *format; va_dcl
  1073. #endif
  1074. /* fatal syntax error */
  1075. {
  1076.     va_list args;
  1077.     oflush();
  1078.     VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
  1079.     vararg_start(args, format);
  1080.     fvfprintf(stderr, format, args);
  1081.     va_end(args);
  1082.     fatcleanup(false);
  1083. }
  1084.  
  1085. #if has_prototypes
  1086.     exiting void
  1087. faterror(char const *format,...)
  1088. #else
  1089.     /*VARARGS1*/ exiting void faterror(format, va_alist)
  1090.     char const *format; va_dcl
  1091. #endif
  1092. /* fatal error, terminates program after cleanup */
  1093. {
  1094.     va_list args;
  1095.     fatsay();
  1096.     vararg_start(args, format);
  1097.     fvfprintf(stderr, format, args);
  1098.     va_end(args);
  1099.     fatcleanup(false);
  1100. }
  1101.  
  1102. #if has_prototypes
  1103.     void
  1104. warn(char const *format,...)
  1105. #else
  1106.     /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
  1107. #endif
  1108. /* prints a warning message */
  1109. {
  1110.     va_list args;
  1111.     oflush();
  1112.     aprintf(stderr,"%s warning: ",cmdid);
  1113.     vararg_start(args, format);
  1114.     fvfprintf(stderr, format, args);
  1115.     va_end(args);
  1116.     afputc('\n',stderr);
  1117.     eflush();
  1118. }
  1119.  
  1120.     void
  1121. redefined(c)
  1122.     int c;
  1123. {
  1124.     warn("redefinition of -%c option", c);
  1125. }
  1126.  
  1127. #if has_prototypes
  1128.     void
  1129. diagnose(char const *format,...)
  1130. #else
  1131.     /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
  1132. #endif
  1133. /* prints a diagnostic message */
  1134. /* Unlike the other routines, it does not append a newline. */
  1135. /* This lets some callers suppress the newline, and is faster */
  1136. /* in implementations that flush stderr just at the end of each printf. */
  1137. {
  1138.     va_list args;
  1139.         if (!quietflag) {
  1140.         oflush();
  1141.         vararg_start(args, format);
  1142.         fvfprintf(stderr, format, args);
  1143.         va_end(args);
  1144.         eflush();
  1145.         }
  1146. }
  1147.  
  1148.  
  1149.  
  1150.     void
  1151. afputc(c, f)
  1152. /* afputc(c,f); acts like aputc_(c,f) but is smaller and slower.  */
  1153.     int c;
  1154.     register FILE *f;
  1155. {
  1156.     aputc_(c,f)
  1157. }
  1158.  
  1159.  
  1160.     void
  1161. aputs(s, iop)
  1162.     char const *s;
  1163.     FILE *iop;
  1164. /* Function: Put string s on file iop, abort on error.
  1165.  */
  1166. {
  1167. #if has_fputs
  1168.     if (fputs(s, iop) < 0)
  1169.         Oerror();
  1170. #else
  1171.     awrite(s, strlen(s), iop);
  1172. #endif
  1173. }
  1174.  
  1175.  
  1176.  
  1177.     void
  1178. #if has_prototypes
  1179. fvfprintf(FILE *stream, char const *format, va_list args)
  1180. #else
  1181.     fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
  1182. #endif
  1183. /* like vfprintf, except abort program on error */
  1184. {
  1185. #if has_vfprintf
  1186.     if (vfprintf(stream, format, args) < 0)
  1187. #else
  1188. #    if has__doprintf
  1189.         _doprintf(stream, format, args);
  1190. #    else
  1191. #    if has__doprnt
  1192.         _doprnt(format, args, stream);
  1193. #    else
  1194.         int *a = (int *)args;
  1195.         VOID fprintf(stream, format,
  1196.             a[0], a[1], a[2], a[3], a[4],
  1197.             a[5], a[6], a[7], a[8], a[9]
  1198.         );
  1199. #    endif
  1200. #    endif
  1201.     if (ferror(stream))
  1202. #endif
  1203.         Oerror();
  1204. }
  1205.  
  1206. #if has_prototypes
  1207.     void
  1208. aprintf(FILE *iop, char const *fmt, ...)
  1209. #else
  1210.     /*VARARGS2*/ void
  1211. aprintf(iop, fmt, va_alist)
  1212. FILE *iop;
  1213. char const *fmt;
  1214. va_dcl
  1215. #endif
  1216. /* Function: formatted output. Same as fprintf in stdio,
  1217.  * but aborts program on error
  1218.  */
  1219. {
  1220.     va_list ap;
  1221.     vararg_start(ap, fmt);
  1222.     fvfprintf(iop, fmt, ap);
  1223.     va_end(ap);
  1224. }
  1225.  
  1226.  
  1227.  
  1228. #ifdef LEXDB
  1229. /* test program reading a stream of lexemes and printing the tokens.
  1230.  */
  1231.  
  1232.  
  1233.  
  1234.     int
  1235. main(argc,argv)
  1236. int argc; char * argv[];
  1237. {
  1238.         cmdid="lextest";
  1239.         if (argc<2) {
  1240.         aputs("No input file\n",stderr);
  1241.         exitmain(EXIT_FAILURE);
  1242.         }
  1243.     if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
  1244.         faterror("can't open input file %s",argv[1]);
  1245.         }
  1246.         Lexinit();
  1247.     while (!eoflex()) {
  1248.         switch (nexttok) {
  1249.  
  1250.         case ID:
  1251.                 VOID printf("ID: %s",NextString);
  1252.                 break;
  1253.  
  1254.         case NUM:
  1255.         if (hshenter)
  1256.                    VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
  1257.                 else
  1258.                    VOID printf("NUM, unentered: %s",NextString);
  1259.                 hshenter = !hshenter; /*alternate between dates and numbers*/
  1260.                 break;
  1261.  
  1262.         case COLON:
  1263.                 VOID printf("COLON"); break;
  1264.  
  1265.         case SEMI:
  1266.                 VOID printf("SEMI"); break;
  1267.  
  1268.         case STRING:
  1269.                 readstring();
  1270.                 VOID printf("STRING"); break;
  1271.  
  1272.         case UNKN:
  1273.                 VOID printf("UNKN"); break;
  1274.  
  1275.         default:
  1276.                 VOID printf("DEFAULT"); break;
  1277.         }
  1278.         VOID printf(" | ");
  1279.         nextlex();
  1280.         }
  1281.     exitmain(EXIT_SUCCESS);
  1282. }
  1283.  
  1284. exiting void exiterr() { _exit(EXIT_FAILURE); }
  1285.  
  1286.  
  1287. #endif
  1288.